package com.yt.dor.api.cms;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.yt.common.constant.Result;
import com.yt.common.enums.CodeEnum;
import com.yt.common.utils.StrUtils;
import com.yt.dor.common.config.YmlConfig;
import com.yt.dor.mapper.DormAllocationMapper;
import com.yt.dor.mapper.DormBedMapper;
import com.yt.dor.mapper.DormRoomMapper;
import com.yt.dor.models.dto.DormBedDTO;
import com.yt.dor.models.dto.DormRoomDTO;

import com.yt.dor.models.vo.DormBedVO;
import com.yt.dor.models.vo.StatisticsVO;
import com.yt.user.models.bo.StudentInfoSaveBO;
import com.yt.user.models.dto.StudentInfoDTO;
import com.yt.user.models.service.IStudentInfoService;
import com.yt.user.models.vo.StudentInfoVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

import com.yt.dor.models.service.IDormAllocationService;
import com.yt.dor.models.bo.DormAllocationSaveBO;
import com.yt.dor.models.bo.DormAllocationBO;
import com.yt.dor.models.dto.DormAllocationDTO;

import javax.annotation.Resource;


/**
 * 宿舍分配表详情控制器
 *
 * @author thekai
 * @Date 2025-10-21 14:10:31
 */

@RestController
    @RequestMapping("/cms/dormAllocation")
@Slf4j
@Api(value = "DormAllocationController",tags = "宿舍分配表详情接口")
public class DormAllocationController {

    @DubboReference
    private IDormAllocationService dormAllocationService;
    @DubboReference
    private IStudentInfoService studentInfoService;
    @Resource
    private DormBedMapper dormBedMapper;
    @Resource
    private DormRoomMapper dormRoomMapper;
    @Resource
    private YmlConfig ymlConfig;
    @Resource
    private DormAllocationMapper dormAllocationMapper;

    @ApiOperation(value = "添加宿舍分配表详情", notes = "添加宿舍分配表详情")
    @PostMapping(value = "/insert")
    public Result insert(DormAllocationSaveBO saveBO) {
        boolean flag = dormAllocationService.add(saveBO);
        if (flag) {
            return new Result<>();
        } else {
            return new Result<>(CodeEnum.FAIL_100007);
        }
    }

    @PostMapping("/save")
    @ApiOperation(value = "保存宿舍分配表详情", notes = "保存宿舍分配表详情")
    public Result save(DormAllocationSaveBO saveBO) {
        if (saveBO.getId() == null) {
            //添加
            return insert(saveBO);
        } else {
            return update(saveBO);
        }
    }

    @ApiOperation(value = "修改宿舍分配表详情", notes = "修改宿舍分配表详情")
    @PostMapping("/update")
    public Result update(DormAllocationSaveBO saveBO) {
        DormAllocationDTO dto = new DormAllocationDTO();
        BeanUtils.copyProperties(saveBO, dto);
        dto.setId(saveBO.getId());
        boolean flag = dormAllocationService.updateById(dto);
        if (flag) {
            return new Result<>();
        } else {
            return new Result<>(CodeEnum.FAIL_100008);
        }
    }

    @PostMapping("/updateStatus")
    @ApiOperation(value = "修改状态", notes = "修改状态")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "id", value = "id", required = true, paramType = "query", dataType = "Long")
            , @ApiImplicitParam(name = "status", value = "", required = true, paramType = "query", dataType = "Integer")
    })
    public Result updateStatus(Long id, Integer status) {
        DormAllocationDTO dto = new DormAllocationDTO();
        dto.setId(id);
        dto.setStatus(status);
        boolean flag = dormAllocationService.updateById(dto);
        if (flag) {
            return new Result<>();
        } else {
            return new Result<>(CodeEnum.FAIL_100008);
        }
    }

    @ApiOperation(value = "删除宿舍分配表详情", notes = "删除宿舍分配表详情")
    @ApiImplicitParam(name = "id", value = "宿舍分配表详情id", required = true, paramType = "query", dataType = "Long")
    @PostMapping("/delete")
    public Result delete(@RequestParam("id") Long id) {
        boolean flag = dormAllocationService.deleteById(id);
        if (flag) {
            return new Result<>();
        } else {
            return new Result<>(CodeEnum.FAIL_100009);
        }
    }

    @PostMapping("/deleteBatch")
    @ApiOperation(value = "批量删除宿舍分配表详情", notes = "批量删除宿舍分配表详情")
    @ApiImplicitParam(name = "ids", value = "ids:以逗号隔开", required = true, paramType = "query", dataType = "String")
    public Result deleteBatch(@RequestParam("ids") String ids) {
        if (StrUtils.isEmpty(ids)) {
            return new Result(CodeEnum.FAIL_100002);
        }
        String[] arr = ids.split(",");
        dormAllocationService.deleteBatchIds(Arrays.asList(arr));
        return new Result<>();
    }

    @PostMapping("/get")
    @ApiOperation(value = "根据id查找", notes = "根据id查找")
    @ApiImplicitParam(name = "id", value = "id", required = true, paramType = "query", dataType = "Long")
    public Result<DormAllocationDTO> get(@RequestParam("id") Long id) {
        if (id == null) {
            return new Result<>(CodeEnum.FAIL_100002);
        }

        DormAllocationDTO dto = dormAllocationService.selectById(id);

        return new Result<>(dto);
    }

    @ApiOperation(value = "宿舍分配表详情列表", notes = "宿舍分配表详情列表")
    @PostMapping("/queryPageList")
    public Result<Page<DormAllocationDTO>> queryPageList(DormAllocationBO bo) {
        Wrapper<DormAllocationDTO> wrapper = new EntityWrapper<>();

        if (bo.getStatus() != null) {
            wrapper.eq("status", bo.getStatus());
        }

        if (bo.getStartDate() != null) {
            wrapper.ge("create_time", bo.getStartDate());
        }
        if (bo.getEndDate() != null) {
            wrapper.le("create_time", bo.getEndDate());

        }
        Page<DormAllocationDTO> page = new Page<>(bo.getPageNo(), bo.getPageSize());
        page = dormAllocationService.selectPage(page, wrapper);

        return new Result<>(page);
    }

    @ApiOperation(value = "宿舍分配表详情列表", notes = "宿舍分配表详情列表")
    @PostMapping("/allocations")
    public void autoAllocateStudents() {
        //分配宿舍
        //1.查询所有未分配的学生
        List<StudentInfoDTO> unallocatedStudents = studentInfoService.selectUnallocatedStudents();
        if (unallocatedStudents.isEmpty()) {
            return;
        }
        // 2. 按性别、年级、专业分组学生
        Map<StudentGroup, List<StudentInfoDTO>> studentGroups = groupStudents(unallocatedStudents);
        // 3. 为每个分组分配床位
        for (Map.Entry<StudentGroup, List<StudentInfoDTO>> entry : studentGroups.entrySet()) {
            StudentGroup group = entry.getKey();
            List<StudentInfoDTO> students = entry.getValue();

            allocateStudentsToBeds(group, students);
        }
    }


    /**
     * 按性别、年级、专业分组学生
     */
    private Map<StudentGroup, List<StudentInfoDTO>> groupStudents(List<StudentInfoDTO> students) {
        return students.stream()
                .collect(Collectors.groupingBy(student ->
                        new StudentGroup(student.getSex(), student.getGrade(), student.getProfessionId())
                ));
    }


    /**
     * 为特定分组的学生分配床位
     */
    private void allocateStudentsToBeds(StudentGroup group, List<StudentInfoDTO> students) {
        // 按优先级查找房间：先找同专业的，再找混合专业的
        List<DormRoomDTO> availableRooms = findAvailableRooms(group);

        for (StudentInfoDTO student : students) {
            boolean allocated = false;

            // 尝试分配床位
            for (DormRoomDTO room : availableRooms) {
                DormBedDTO availableBed = dormBedMapper.selectAvailableBedByRoomId(room.getId());

                if (availableBed != null) {
                    allocateBedToStudent(room, availableBed, student, group);
                    allocated = true;
                    break;
                }
            }

            if (!allocated) {
                // 如果没有找到合适床位，记录日志或处理异常
                log.warn("无法为学生 {} 分配床位，分组: {}", student.getName(), group);
            }
        }
    }


    /**
     * 查找可用的房间
     */
    private List<DormRoomDTO> findAvailableRooms(StudentGroup group) {
        List<DormRoomDTO> rooms = new ArrayList<>();

        // 1. 先查找同专业的房间
        List<DormRoomDTO> sameProfessionRooms = dormRoomMapper.selectAvailableRoomsByProfession(
                group.getSex(), group.getProfessionId(), group.getGrade());
        rooms.addAll(sameProfessionRooms);

        // 2. 再查找混合专业的房间
        List<DormRoomDTO> mixedRooms = dormRoomMapper.selectAvailableMixedRooms(
                group.getSex(), group.getGrade());
        rooms.addAll(mixedRooms);

        return rooms;
    }


    /**
     * 将床位分配给学生
     */
    private void allocateBedToStudent(DormRoomDTO room, DormBedDTO bed, StudentInfoDTO student, StudentGroup group) {
        // 1. 更新床位信息
        updateBedAllocation(bed, student.getId());

        // 2. 创建分配记录
        createAllocationRecord(room, bed, student);

        // 3. 检查并更新房间的专业混合状态
        updateRoomMixedStatus(room, group.getProfessionId());

        // 4. 更新房间状态（如果住满）
        updateRoomStatus(room);
    }


    private void updateBedAllocation(DormBedDTO bed, Long studentId) {
        bed.setStudentId(studentId);
        bed.setStatus(1); // 设置为已占用
        bed.setUpdateTime(new Date());
        dormBedMapper.updateById(bed);
    }


    private void createAllocationRecord(DormRoomDTO room, DormBedDTO bed, StudentInfoDTO student) {
        DormAllocationDTO allocation = new DormAllocationDTO();
        Long id = ymlConfig.getPkId();
        allocation.setId(id);
        allocation.setBuildingId(room.getBuildingId());
        allocation.setRoomId(room.getId());
        allocation.setBedId(bed.getId());
        allocation.setStudentId(student.getId());
        allocation.setStatus(1); // 已生效
        allocation.setCreateTime(new Date());
        allocation.setUpdateTime(new Date());

        dormAllocationMapper.insert(allocation);
    }


    /**
     * 更新房间混合专业状态
     */
    private void updateRoomMixedStatus(DormRoomDTO room, String studentProfessionId) {
        // 如果房间的专业ID不为空且与当前学生专业不同，则设置为混合专业
        if (room.getProfessionId() != null && !room.getProfessionId().equals(studentProfessionId)) {
            // 检查房间内是否已有其他专业的学生
            List<Long> studentProfessionsInRoom = dormBedMapper.selectStudentProfessionsInRoom(room.getId());

            boolean hasMultipleProfessions = studentProfessionsInRoom.stream()
                    .filter(Objects::nonNull)
                    .distinct()
                    .count() > 1;

            if (hasMultipleProfessions) {
                room.setProfessionId(null);
                room.setIsMix(1); // 设置为混合专业
                room.setUpdateTime(new Date());
                dormRoomMapper.updateById(room);
            }
        }
    }


    /**
     * 更新房间状态
     */
    private void updateRoomStatus(DormRoomDTO room) {
        int occupiedBedCount = dormBedMapper.countOccupiedBedsByRoomId(room.getId());

        if (occupiedBedCount >= room.getCount()) {
            // 房间已住满
            room.setRoomStatus(1);
        } else if (occupiedBedCount > 0) {
            // 房间有学生入住但未住满
            room.setRoomStatus(0); // 空闲状态，但实际有学生
        }

        room.setUpdateTime(new Date());
        dormRoomMapper.updateById(room);
    }


    /**
     * 学生分组类
     */
    @Data
    @AllArgsConstructor
    private static class StudentGroup {
        private String sex;          // 性别
        private String grade;        // 年级
        private String professionId;    // 专业ID

        // 重写equals和hashCode用于分组
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            StudentGroup that = (StudentGroup) o;
            return Objects.equals(sex, that.sex) &&
                    Objects.equals(grade, that.grade) &&
                    Objects.equals(professionId, that.professionId);
        }

        @Override
        public int hashCode() {
            return Objects.hash(sex, grade, professionId);
        }
    }


    @Scheduled(cron = "0 0 2 * * ?")
    @PostMapping("/statistics")
    @ApiOperation(value = "后台管理统计", notes = "后台管理统计统计")
    public Result<StatisticsVO> Statistics() {
        StatisticsVO statisticsVO = dormAllocationService.queryStatis();
        return new Result<>(statisticsVO);
    }

    @ApiOperation(value = "查询未分配学生", notes = "查询未分配学生")
    @PostMapping("/selectUnallocatedStudents")
    public Result<List<StudentInfoVO>> queryUnallocatedStudents() {
        //1.查询所有未分配的学生
        List<StudentInfoDTO> unallocatedStudents = studentInfoService.selectUnallocatedStudents();
        List<StudentInfoVO> unallocatedStudentsVO = new ArrayList<>();

        for (StudentInfoDTO dto : unallocatedStudents) {
            StudentInfoVO vo = new StudentInfoVO();
            BeanUtils.copyProperties(dto, vo);
            unallocatedStudentsVO.add(vo);
        }
        return  new Result<>(unallocatedStudentsVO);
    }
    @ApiOperation(value = "查询未分配的床位", notes = "查询未分配的床位")
    @PostMapping("/selectUnallocatedBeds")
    public Result<List<DormBedVO>> queryUnallocatedBeds() {
        //查询未分配的床位
        List<DormBedVO> unallocatedBeds = dormBedMapper.selectUnallocatedBeds();
        return new Result<>(unallocatedBeds);
    }




    @ApiOperation(value = "手动分配", notes = "手动分配")
    @PostMapping("/manual assignment")
    public Result manualAssignment(@RequestBody DormAllocationSaveBO bo) {
        //创建分配记录
        boolean flag = dormAllocationService.add(bo);
        if (flag) {
            // 1. 创建查询用的实体对象
            DormBedDTO queryBed = new DormBedDTO();
            queryBed.setId(bo.getBedId()); // 设置查询条件：id = bo.getBedId()

            // 2. 执行查询
            DormBedDTO bed = dormBedMapper.selectOne(queryBed);

            if (bed != null) { // 防止查询结果为空导致空指针
                bed.setStatus(1); // 状态从0改为1
                bed.setStudentId(bo.getStudentId()); // 设置学生id
                dormBedMapper.updateById(bed);
            }
            return new Result<>();
        } else {
            return new Result<>(CodeEnum.FAIL_100007);
        }









    }
}





